package org.cagui.core;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;

import javax.security.auth.x500.X500Principal;

import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.cagui.util.StringUtils;

public class CertificateManager {

	public static final String defaultCertificatesPath = "./certificates/";
	
	private KeyStoreManager keyStoreManager = new KeyStoreManager();
	
	/**
	 * 
	 * Store all args in params. If there is sth missing in that class create it ;).
	 * 
	*/
	@SuppressWarnings("deprecation")
	public void generateNewCertificate(CertificateParams params) throws InvalidKeyException,
    NoSuchProviderException, SignatureException, NoSuchAlgorithmException, IOException, KeyStoreException, CertificateException{
		
	    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");  
	    keyPairGenerator.initialize(params.getRsaKeyLength());  
	    
	    KeyPair pair = keyPairGenerator.generateKeyPair();  
	    System.out.println("New KeyPair generated.");
		
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

	    X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();

	    certGen.setSerialNumber(BigInteger.valueOf(Math.abs(new SecureRandom().nextInt())));  
	    
	    /*
	     Key     Attribute (X.520 keys)
                       ------------------------------
                       CN      CommonName
                       L       LocalityName
                       ST      StateOrProvinceName
                       O       OrganizationName
                       OU      OrganizationalUnitName
                       C       CountryName
                       STREET  StreetAddress
	     */
	    
	    String name = "CN=" + params.getAlias() + 
	    		 	  ", L=" + params.getLocality() +
	    		 	  ", ST=" + params.getState() +
	    		 	  ", O=" + params.getOrganizationName() + 
	    		 	  ", OU=" + params.getOrganizationUnit() + 
	    		 	  ", C=" + params.getCountry();
	    
	    certGen.setIssuerDN(new X500Principal(name));
	    certGen.setNotBefore(new Date(System.currentTimeMillis()));
	    certGen.setNotAfter(new Date(System.currentTimeMillis() + ( params.getExpireDataMilis() * 60 * 60 * 1000 )));
	    certGen.setSubjectDN(new X500Principal(name));
	    
	    certGen.setPublicKey(pair.getPublic());
	    certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");

	    X509Certificate PKCertificate = certGen.generateX509Certificate(pair.getPrivate());  
	    
	    FileOutputStream fos = new FileOutputStream(defaultCertificatesPath + params.getFileNameForCertificate() + ".cert");  
	    fos.write(PKCertificate.getEncoded());  
	    fos.close();  
	    
	    System.out.println("Certificate was generated and saved into file: " + params.getFileNameForCertificate() + ".cert");
	    
	    System.out.println("Opening keyStore: " + KeyStoreManager.path + params.getKeystoreName());	    
	    
	    
	    KeyStore privateKS = keyStoreManager.loadKeyStore(KeyStoreManager.path+ params.getKeystoreName(), params.getKeyStorePassword());
	    
	    privateKS.setKeyEntry(params.getAlias(), pair.getPrivate(),  
                params.getPrivateKeyPassword().toCharArray(),  
                new java.security.cert.Certificate[]{PKCertificate});  
	    
	    privateKS.store( new FileOutputStream(KeyStoreManager.path+ params.getKeystoreName()), params.getKeyStorePassword().toCharArray()); 	    
	    
	    System.out.println("Private key was stored in " + KeyStoreManager.path+ params.getKeystoreName());    
	}
	
	public void importCertificate(String fromPath) throws IOException{
		File f1 = new File(fromPath);			
		File f2 = new File(defaultCertificatesPath + StringUtils.getFileNameFromPath(fromPath));		
		
		InputStream in = new FileInputStream(f1);
		OutputStream out = new FileOutputStream(f2);

		byte[] buf = new byte[1024];
		int len;
		while ((len = in.read(buf)) > 0) {
			
			out.write(buf, 0, len);
		}
		
		in.close();
		out.close();
		
		System.out.println("Certificate added to certificate directory.");
	}
	
	public void removeCertificate(String fileName){
	    File f = new File(defaultCertificatesPath + fileName);

	    // Make sure the file or directory exists and isn't write protected
	    if (!f.exists())
	      throw new IllegalArgumentException(
	          "Delete: no such file or directory: " + fileName);

	    if (!f.canWrite())
	      throw new IllegalArgumentException("Delete: write protected: "+ fileName);

	    // Attempt to delete it
	    boolean success = f.delete();

	    if (!success)
	      throw new IllegalArgumentException("Delete: deletion failed");
	    
	    System.out.println("Certificate deleted.");
	}
	
	public String readCertificateFromFile(String fileName)
	{
		try {
	        FileInputStream is = new FileInputStream(defaultCertificatesPath + fileName);

	        CertificateFactory cf = CertificateFactory.getInstance("X.509");
	        X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
	        
	        String returnValue = "Certificate filename: " + fileName +
	        		"\nPrincipal data: " + cert.getSubjectX500Principal().toString() +
	        		"\nVersion: " + cert.getVersion() +
	        		"\nSerial number: " + cert.getSerialNumber().toString() +
	        		"\nValid not before: " + cert.getNotAfter().toString() + 
	        		"\nValid not after: " + cert.getNotBefore().toString() +
	        		"\n\nPublic key:\n " + cert.getPublicKey().toString();
	        
	        return returnValue;
	        
	        
	    } catch (CertificateException e) {
	    } catch (IOException e) {
	    }
		
		return "";
	}
}
